/*
 * Copyright (c) 2012 Loren M Halvorson
 * This source is subject to the Microsoft Public License (Ms-PL).
 * See http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx.
 * All other rights reserved.
 * Portions copyright 2002-2007 The Genghis Group (http://www.genghisgroup.com/)
 */

using System;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text.RegularExpressions;

namespace ScrapeDocs
{
    /// <summary>
    /// 
    /// </summary>
    public class HtmlPageWriter
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="HtmlPageWriter"/> class.
        /// </summary>
        public HtmlPageWriter()
        {
        }


        /// <summary>
        /// Writes the HTML.
        /// </summary>
        /// <param name="title">The title.</param>
        /// <param name="body">The body.</param>
        /// <param name="templatePath">The template path.</param>
        /// <param name="outputFolder">The output folder.</param>
        public void WriteHtml(string title, string body, string templatePath, string outputFolder)
        {
            string fileName = title + ".html";

            string templateText = null;
            if (!string.IsNullOrEmpty(templatePath))
                templateText = LoadFile(templatePath);
            else
                templateText = LoadFromResource("Template.html");

            string content = templateText;

            content = content.Replace("@TITLE@", title);
            content = content.Replace("@BODY@", body);
            content = Regex.Replace(content, "/wikipage\\?title=(?<title>[^&]*)&[^\"]*\"", "${title}.html\"");


            string outputPath = outputFolder ?? "";
            if (string.IsNullOrEmpty(outputPath) || !Path.IsPathRooted(outputPath))
            {
                outputPath = Path.Combine(Environment.CurrentDirectory, outputPath);
            }

            if (!Directory.Exists(outputPath))
            {
                Directory.CreateDirectory(outputPath);
            }

            if (content.Contains("<img "))
            {
                content = DownloadImages(content, outputPath);
            }

            WriteFile(Path.Combine(outputPath, fileName), content);
        }


        /// <summary>
        /// Downloads the images and replaces the src attribute to a local file reference.
        /// </summary>
        /// <param name="content">The content.</param>
        /// <param name="outputPath">The Output Path.</param>
        /// <returns></returns>
        private string DownloadImages(string content, string outputPath)
        {
            // html page may contain something like:
            // <img title="abc.gif" alt="abc.gif" src="http://i3.codeplex.com/Download?ProjectName=xmlpreprocess&DownloadId=48433"/>
            // this regular expression goodness below isolates the file name and the URL
            // then downloads the image locally, and replaces the img tag with a local file reference

            const string patternImg = "(?<img><img title=\\\")(?<title>[^\\\"]*)(?<alt>\\\".*?src=\\\")(?<url>[^\\\"]*)\\\">";
            const string replacementImg = "${img}${title}${alt}${title}\">";
            Regex regexImg = new Regex(patternImg, RegexOptions.Singleline | RegexOptions.IgnoreCase);

            for (Match matchImg = regexImg.Match(content);
                matchImg.Success;
                matchImg = matchImg.NextMatch())
            {
                Group titleGroup = matchImg.Groups["title"];
                Group urlGroup = matchImg.Groups["url"];
                Group altGroup = matchImg.Groups["alt"];

                if ((null != urlGroup && !string.IsNullOrEmpty(urlGroup.Value)) &&
                    (null != titleGroup && !string.IsNullOrEmpty(titleGroup.Value)))
                {
                    string url = urlGroup.Value.Trim();
                    string fileName = titleGroup.Value.Trim();

                    Console.WriteLine(" Downloading Image {0} to {1}", url, fileName);

                    DownloadImage(url, Path.Combine(outputPath, fileName));
                }
            }

            return Regex.Replace(content, patternImg, replacementImg);
        }


        /// <summary>
        /// Downloads the image to a local file
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <param name="fileName">Name of the file.</param>
        public void DownloadImage(string url, string fileName)
        {
            byte[] buffer;
            byte[] fileBytes;

            HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
            using (WebResponse webResponse = httpWebRequest.GetResponse())
            {
                using (BinaryReader br = new BinaryReader(webResponse.GetResponseStream()))
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        buffer = br.ReadBytes(1024);
                        while (buffer.Length > 0)
                        {
                            ms.Write(buffer, 0, buffer.Length);
                            buffer = br.ReadBytes(1024);
                        }
                        fileBytes = new byte[(int)ms.Length];
                        ms.Position = 0;
                        ms.Read(fileBytes, 0, fileBytes.Length);
                        ms.Close();
                        br.Close();
                    }
                }
                webResponse.Close();
            }

            using (FileStream fs = new FileStream(fileName, FileMode.Create))
            {
                fs.Write(fileBytes, 0, fileBytes.Length);
                fs.Close();
            }
        }


        /// <summary>
        /// Writes a file.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="content">The content.</param>
        private void WriteFile(string path, string content)
        {
            if (!string.IsNullOrEmpty(path) && null != content)
            {
                using (StreamWriter stream = File.CreateText(path))
                {
                    stream.Write(content);
                }
            }
        }


        /// <summary>
        /// Loads a file into a string.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns>the contents of the file</returns>
        private string LoadFile(string path)
        {
            string fileContents;

            using (StreamReader reader = File.OpenText(path))
            {
                fileContents = reader.ReadToEnd();
            }

            return fileContents;
        }


        /// <summary>
        /// Loads a string from an embedded resource.
        /// </summary>
        /// <param name="resource">The resource.</param>
        /// <returns></returns>
        private string LoadFromResource(string resource)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            using (StreamReader reader = new StreamReader(assembly.GetManifestResourceStream("ScrapeDocs." + resource)))
            {
                return reader.ReadToEnd();
            }
        }
    }
}
